using Taxhui.Utils.Network.Delegate;
using Taxhui.Utils.Network.Tcp.Awaitable;
using Taxhui.Utils.Network.Tcp.Pooling;
using Taxhui.Utils.Network.Tcp.TcpConfig;
using Taxhui.Utils.Network.UtilityHelper;
using System;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using Taxhui.Utils.Network.CustomProc;

namespace Taxhui.Utils.Network.Tcp.NetSession
{
    public class FullSessionBased : Session
    {
        internal FullSessionBased(
            ConfigBase cfg,
            AwaiterPool awaiterPool,
            SessionPool sessionPool,
            NotifyEventHandler<CompleteNotify, Session> notifyEventHandler,
            LogHelper logHelper,
            NetEngineBased agent,
            ICustomDataProc headProc = null)
            : base(notifyEventHandler, cfg, awaiterPool, sessionPool, agent, logHelper, headProc) 
            => _completebuffer = new byte[cfg.ReceiveBufferSize];
        #region 附加SOCKET
        internal override void Attach(Socket socket)
        {
            if (socket == null)
                throw new ArgumentNullException("socket NULL");

            lock (_opsLock)
            {
                _state = ConnectionState.Connected;
                _socket = socket;
                _startTime = DateTime.UtcNow;
                SetSocketOptions();
            }
        }
        #endregion
        #region 分离SOCKET
        internal override void Detach()
        {
            lock (_opsLock)
            {
                _socket = null;
                _state = ConnectionState.None;
                AppTokens = null;
            }
        }
        #endregion
        #region 设置SOCKET选项
        private void SetSocketOptions()
        {
            _socket.ReceiveBufferSize = _configuration.ReceiveBufferSize;
            _socket.SendBufferSize = _configuration.SendBufferSize;
            _socket.ReceiveTimeout = (int)_configuration.ReceiveTimeout.TotalMilliseconds;
            _socket.SendTimeout = (int)_configuration.SendTimeout.TotalMilliseconds;
            _socket.NoDelay = _configuration.NoDelay;

            if (_configuration.AutoHeartBeat)
                SetKeepAlive(_socket, 1, _configuration.KeepAliveInterval, _configuration.KeepAliveSpanTime);

            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
        }
        #endregion
        #region 设置ACK Keep-Alive选项
        private void SetKeepAlive(Socket sock, byte op, int interval, int spantime)
        {
            uint dummy = 0;
            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
            BitConverter.GetBytes(op).CopyTo(inOptionValues, 0);//开启keepalive
            BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多长时间开始第一次探测
            BitConverter.GetBytes((uint)spantime).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探测时间间隔
            sock.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
        }
        #endregion
        #region 开始接收数据
        internal override void StartProcess()
        {
            var awaiter = _awaiterPool.Take();
            awaiter.Args.SetBuffer(_completebuffer, 0, _completebuffer.Length);
            NetHelper.ReceiveAsync(_socket, awaiter, RecvCallback);
        }
        #endregion
        #region 收到数据发送通知
        private void RecvCallback(Awaiter awaiter, SocketError error)
        {
            var bytesTransferred = awaiter.Args.BytesTransferred;
            if (bytesTransferred == 0 ||
                error != SocketError.Success ||
                _state != ConnectionState.Connected ||
                _socket == null)
            {
                _logger.WriteLog("接收数据出错，会话结束 state：" + _state.ToString() + " socket_error：" + error.ToString());
                EndTransfer(awaiter);
                return;
            }
            ReceiveBytesTransferred = bytesTransferred;

            _notifyEventHandler?.Invoke(CompleteNotify.OnDataReceiveing, this);

            awaiter.Args.SetBuffer(_completebuffer, 0, _completebuffer.Length);
            NetHelper.ReceiveAsync(_socket, awaiter, RecvCallback);
        }
        #endregion
        #region 结束会话
        private void EndTransfer(Awaiter awaiter)
        {
            Close(true);
            _awaiterPool.Return(awaiter);
            _sessionPool.Return(this);
        }
        #endregion
        #region 发送数据
        public override void SendAsync(byte[] data)
        {
            SendAsync(data, 0, data.Length);
        }

        public override void SendAsync(byte[] data, int offset, int lenght)
        {
            if (_socket == null)
                return;

            var awaiter = _awaiterPool.Take();
            awaiter.Args.SetBuffer(data, offset, lenght);

            NetHelper.SendAsync(_socket, awaiter, (a, e) =>
            {
                SendTransferredBytes = a.Args.Buffer.Length;

                _awaiterPool.Return(awaiter);
                _notifyEventHandler?.Invoke(CompleteNotify.OnSend, this);
            });
        }
        #endregion
        #region 关闭连接
        public override void Close(bool notify)
        {
            lock (_opsLock)
            {
                if (_socket != null)
                {
                    try
                    {
                        _socket.Shutdown(SocketShutdown.Both);
                        _socket.Close();
                    }
                    catch (Exception e)
                    {
                        _logger.WriteLog("会话关闭 info：" + e.Message);
                    }
                    finally
                    {
                        _socket = null;
                    }
                    _state = ConnectionState.Closed;

                    if (notify)
                    {
                        _notifyEventHandler?.Invoke(CompleteNotify.OnClosed, this);
                    }
                }
            }
        }
        #endregion
    }
}
